# @Time : 2024-09-27 13:51
# @Author : Fioman 
# @Phone : 13149920693
"""
数据库操作类,使用单例模式,这个类主要用来访问和操作数据库来使用的
"""
import threading

import pymongo

from app_config import database_name
from common.data_result import DataResult
from common.datetime_helper import get_datetime_with_hms, get_datetime_for_logger_dir
from common.enum_model import RoleType
from common.produce_logger import ProduceLogger
from db_tools.db_data_manager import dump_data

logger = ProduceLogger()


class DbHandler(object):
    """
    数据库操作类
    """
    url = "mongodb://localhost:27017/"
    db_name = database_name
    __instance = None
    __init_flag = False
    __lock = threading.Lock()

    def __new__(cls, *args, **kwargs):
        if cls.__instance is None:
            with cls.__lock:
                cls.__instance = super(DbHandler, cls).__new__(cls, *args, **kwargs)
        return cls.__instance

    def __init__(self):
        if not self.__init_flag:
            print("DbHanlder.__init__() 进行初始化")
            self.__init_flag = True
            self.client = pymongo.MongoClient(self.url)
            self.params_db = self.client[self.db_name]
            # 统计所有的数据库表格,如果不存在就创建
            table_names = self.params_db.list_collection_names()

            self.order_table = "order"
            if self.order_table not in table_names:
                data = {
                    "key": "1",
                    "order_id": "default_test",
                    "width": 600,
                    "height": 600,
                    "cropper_thickness": "0~0.33",
                    "top_edge": 15,
                    "left_edge": 15,
                    "bottom_edge": 15,
                    "right_edge": 15,
                    "calc_height": 0,
                    "calc_width": 0,
                    "is_break": "否",
                    "layout": "竖排",
                    "amount": 0,
                    "joint_number": "2",
                }
                self.insert_data(self.order_table, data)

            self.offline_params_table = "offline_zmc_params"
            if self.offline_params_table not in table_names:
                data = {
                    "key": "1"
                }
                self.insert_data(self.offline_params_table, data)

            self.cam_zmc_table = "cam_zmc_params"
            if self.cam_zmc_table not in table_names:
                data = {
                    "key": "1"
                }
                self.insert_data(self.cam_zmc_table, data)

            self.cbj_zmc_table = "cbj_zmc_params"
            if self.cbj_zmc_table not in table_names:
                data = {
                    "key": "1"
                }
                self.insert_data(self.cbj_zmc_table, data)

            self.cutter_zmc_table = "cutter_zmc_params"
            if self.cutter_zmc_table not in table_names:
                data = {
                    "key": "1"
                }
                self.insert_data(self.cutter_zmc_table, data)

            # 线阵相机参数设置
            self.line_camera_params = "line_camera_params"
            if self.line_camera_params not in table_names:
                data = {
                    "key": "1",
                    "camera_width": 2048,
                    "camera_height": 4000,
                    "height_wid_ratio": 1,
                    "mm2pix": 3.1,  # 像素比,就是像素转换为mm的比例
                    "mid_joint_pos01": 1100,  # 中缝拼接处1
                    "mid_joint_pos02": 1300,  # 中缝拼接处2
                    "sensor_pos": 100,  # 传感器触发位置
                    "table_pos_start": 100,  # 台布起始位置
                    "table_pos_end": 200,  # 台布结束位置
                    "table_val_thres": 20,  # 台布亮度阈值
                    "sensor_val_thres": 15,  # 板子亮度阈值
                    "size_scale": 2.5,  # 图像缩放比例
                    "height_max": 1400,  # 物料最大高度
                    "height_min": 900,  # 物料最小高度
                    "sensor_dis_error": 10,  # 拍照触发阈值
                    "calc_thres_big": 15,  # 识别偏大阈值
                    "calc_thres_small": 10,  # 识别偏小阈值
                    "first_board_error": 13,  # 第一块阈值
                    "second_board_error": 13,  # 第二块阈值
                    "total_board_error": 18,  # 总长阈值
                }
                self.insert_data(self.line_camera_params, data)

            # 相机的Ip地址配置信息
            self.camera_ip_table = "camera_ip_config"
            if self.camera_ip_table not in table_names:
                data = {
                    "key": "1",
                    "left": {
                        "ip": "192.169.1.20",
                        "net_ip": "192.169.1.2"
                    },
                    "middle": {
                        "ip": "192.170.1.20",
                        "net_ip": "192.170.1.2"
                    },
                    "right": {
                        "ip": "192.171.1.20",
                        "net_ip": "192.171.1.2"
                    },
                    "cutter01": {
                        "ip": "192.172.1.20",
                        "net_ip": "192.172.1.2"
                    },
                    "cutter02": {
                        "ip": "192.173.1.20",
                        "net_ip": "192.173.1.2"
                    }
                }
                self.insert_data(self.camera_ip_table, data)

            # 相机的拼接参数,图像截取
            self.joint_params_table = "image_joint_params"
            if self.joint_params_table not in table_names:
                data = {
                    "key": "1",
                    "left_cam_start_offset": 0,
                    "left_cam_end_offset": 0,
                    "mid_cam_start_offset": 0,
                    "mid_cam_end_offset": 0,
                    "right_cam_start_offset": 0,
                    "right_cam_end_offset": 0,
                    "left_cam_height_offset": 0,
                    "right_cam_height_offset": 0,
                }
                self.insert_data(self.joint_params_table, data)

            # 作业信息参数,当前作业的信息,主要用来显示,比如生产数量,铜箔面积,切割面积,刀偏寿命,台布使用时间
            self.working_info_table = "working_info"
            if self.working_info_table not in table_names:
                data = {
                    "key": "1",
                    "order_used_key": "1",
                    "order_used_id": "",
                    "work_index": 0,
                    "table_used": 0,
                    "table_start_day": get_datetime_for_logger_dir(),
                    "cut_areas": 0,
                    "cut_speed": 800,
                    "cut_deep": 40,
                    "cut_amount": 0,  # 总数量
                    "index_max": 10000,
                    "knife_max": 40000,
                    "knife_used": 0,
                }
                self.insert_data(self.working_info_table, data)

            # 台面补偿的数据表,这个要根据底层的数据去看,和底层设置的有关系,具体要现场去看
            self.capfile_data_table = "capfile_data_params"
            if self.capfile_data_table not in table_names:
                data = {
                    "key": "1",
                    "capfile_data": [],
                    "x_min": 0,
                    "y_min": 0,
                    "x_interval": 0,
                    "y_interval": 0,
                    "x_nums": 0,
                    "y_nums": 0
                }
                self.insert_data(self.capfile_data_table, data)

            self.userinfo_table = "userinfos"
            if self.userinfo_table not in table_names:
                data = {
                    "key": "1",
                    "username": "user",
                    "password": "hd",
                    "role": RoleType.普通人员.name,
                    "register_time": get_datetime_with_hms(),
                    "last_login_time": get_datetime_with_hms()
                }
                self.insert_data(self.userinfo_table, data)

            # 切割参数表
            self.knife_cut_table = "knife_cut_params"
            if self.knife_cut_table not in table_names:
                data = {
                    "key": "1",
                    "speed_lv01": 900,
                    "speed_lv02": 900,
                    "speed_lv03": 900,
                    "speed_lv04": 900,
                    "speed_lv05": 600,
                    "first_cut_rate": 0.8,
                    "cut_deep": 32,
                    "knife_angle_offset": 0
                }
                self.insert_data(self.knife_cut_table, data)

            # 铡刀相关参数
            self.cutter_cam_check_thres = "cutter_cam_check_thres"
            if self.cutter_cam_check_thres not in table_names:
                data = {
                    "key": "1",
                    "begin_angle_thres": 0.2,
                    "begin_angle_offset": 0,
                    "move_offset": 0,
                    "final_first_board_thres": 2.5,
                    "final_second_board_thres": 10,
                    "final_total_board_thres": 18,
                    "final_front_angle_thres": 1,
                    "final_back_angle_thres": 1,
                    "final_fold_thres": 10,  # 终检叠板阈值
                }
                self.insert_data(self.cutter_cam_check_thres, data)

            # 标定相关的参数
            self.trans_mtx_table = "trans_mtx"
            if self.trans_mtx_table not in table_names:
                data = {
                    "key": "1",
                    "mtx": [
                        [1, 0, 1],
                        [0, 1, 0],
                    ],
                    "mtx_rotate": [
                        [1, 0, 1],
                        [0, 1, 0],
                    ]
                }
                self.insert_data(self.trans_mtx_table, data)

    def insert_data(self, table_name, data):
        """
        传入的数据的数据类型可能是列表,如果是列表,列表里的每一项都是字典,认为是插入了多条记录,否则就认为是插入一条记录
        支持插入多条记录
        :param table_name:
        :param data:
        :return: DataResult
        """
        result = DataResult()
        try:
            if isinstance(data, list) and len(data) > 0 and isinstance(data[0], dict):
                self.params_db[table_name].insert_many(data)
            elif isinstance(data, dict):
                self.params_db[table_name].insert_one(data)
            else:
                result.state = False
                result.info = f"in DbHandler.insert_data() 中 data 的类型错误,应该为字典类型,实际插入的类型为{type(data)}"
        except Exception as e:
            result.state = False
            result.info = ("in in DbHandler.insert_data()  error: {}".format(str(e)))
        finally:
            if not result.state:
                logger.debug(result.info)
            return result

    def get_data_one(self, table_name, query=None, projection=None, sort="", direction=1):
        """
         根据数据库的表名,以及查询条件,获取这个数据库表中的一条数据,一般这个情况数据库中也只有唯一的一条数据
        :param table_name:数据库表名
        :param query:查询条件
        :param projection:过滤条件
        :param sort:排序
        :param direction:方向
        :return: 1. 成功返回"ok",查找到的数据列表,如果查找的数据列表是空   2. 失败返回"error",以及失败原因
        :param query:
        :param projection:
        :param sort:
        :param direction:
        :return:
        """
        result = DataResult()
        find_data = []
        try:
            if query is None:
                query = {}
            if projection is None:
                projection = {
                    "_id": 0,
                    "time": 0
                }
            if sort == "":
                find_data = list(self.params_db[table_name].find(query, projection))
            else:
                find_data = list(self.params_db[table_name].find(query, projection).sort(sort, direction))
        except Exception as e:
            errorInfo = "in DbHandler.get_data(),error : {}".format(str(e))
            result.info = errorInfo
            result.state = False
        finally:
            result.data = find_data[0] if len(find_data) >= 1 else find_data
            if result.state:
                if len(find_data) == 0:
                    result.state = False
                    result.info = f"数据表 {table_name},查找到的数据为空"
            return result

    def get_data_list(self, table_name, query=None, projection=None, sort="", direction=1):
        """
        根据数据库表名,以及查询条件,获取这个数据表中的所有的数据,以列表的形式返回
        :param table_name:
        :param query:
        :param projection:
        :param sort:排序
        :param direction:方向
        :return:
        """
        result = DataResult()
        try:
            if query is None:
                query = {}
            if projection is None:
                projection = {
                    "_id": 0,
                    "time": 0
                }
            if sort == "":
                find_data = list(self.params_db[table_name].find(query, projection))
            else:
                find_data = list(self.params_db[table_name].find(query, projection).sort(sort, direction))
            result.data = find_data
        except Exception as e:
            result.state = False
            result.info = "in get_data_list() error: {}".format(str(e))
        finally:
            return result

    def update_data(self, table_name, query, new_values, update_one=False):
        """
        更新数据,根据query筛选条件去更新数据
        :param table_name:
        :param query:
        :param new_values:
        :return:
        """
        result = DataResult()
        new_values = {
            "$set": new_values
        }
        try:
            if update_one:
                self.params_db[table_name].update_one(query, new_values)
            else:
                self.params_db[table_name].update_many(query, new_values)
        except Exception as e:
            result.state = False
            result.info = "in update_data() error: {}".format(str(e))
        finally:
            return result

    def get_camera_ip(self):
        """
        获取相机的Ip地址,按照相机的名称进行返回,比如left_cam,right_cam,mid_cam,cutter_cam01,cutter_cam02
        :return:
        """
        table_name = self.camera_ip_table
        query = {
            "key": "1"
        }
        result = self.get_data_one(table_name, query)
        if result.state:
            camera_ips = result.data
            left_ips = camera_ips.get("left").get("ip"), camera_ips.get("left").get("net_ip")
            middle_ips = camera_ips.get("middle").get("ip"), camera_ips.get("middle").get("net_ip")
            right_ips = camera_ips.get("right").get("ip"), camera_ips.get("right").get("net_ip")
            cutter_ips01 = camera_ips.get("cutter01").get("ip"), camera_ips.get("cutter01").get("net_ip")
            cutter_ips02 = camera_ips.get("cutter02").get("ip"), camera_ips.get("cutter02").get("net_ip")
            result.data = [left_ips, middle_ips, right_ips, cutter_ips01, cutter_ips02]
        return result

    def get_camera_params(self):
        """
        获取相机的参数配置
        :return:
        """
        table_name = self.line_camera_params
        query = {
            "key": "1"
        }
        return self.get_data_one(table_name, query)

    def get_image_jont_params(self):
        """
        获取拼接图像的参数,主要是图像截取
        :return:
        """
        table_name = self.joint_params_table
        query = {
            "key": "1"
        }
        return self.get_data_one(table_name, query)

    def get_working_info(self):
        """
        获取当前的作业信息
        :return:
        """
        table_name = self.working_info_table
        query = {
            "key": "1"
        }
        return self.get_data_one(table_name, query)

    def get_order_used_key(self):
        """
        获取正在作业的工单的key值,如果这个key值不存在,就去随机获取一个工单的key设置为当前作业的key.
        :return:
        """
        result = DataResult()
        try:
            result = self.get_working_info()
            if result.state:
                working_key = result.data.get("order_used_key", None)
                if working_key is None:
                    order_val = self.get_total_orders().data[0]
                    working_key = order_val.get("key")
                    new_values = {
                        "order_used_key": working_key,
                        "order_used_id": order_val.get("order_used_id")
                    }
                    self.upadte_working_info(new_values)
                result.data = working_key
        except Exception as e:
            result.state = False
            result.info = (" in get_order_used_key() error: {}".format(str(e)))
        finally:
            return result

    def get_order_by_key(self, order_key):
        """
        根据作业的key获取当前作业的工单,返回单个工单
        :return:
        """
        table_name = self.order_table
        query = {
            "key": order_key
        }
        result = DataResult()
        try:
            result = self.get_data_one(table_name, query)
        except Exception as e:
            result.state = False
            result.info = f" in get_order_by_key() error: {str(e)}"
        finally:
            return result

    def get_total_orders(self):
        """
        获取所有的工单数据表
        :return:
        """
        table_name = self.order_table
        result = DataResult()
        try:
            query = {}
            result = self.get_data_list(table_name, query)
            if result.state:
                if len(result.data) == 0:
                    result.data = {
                        "key": "1",
                        "order_id": "default_test",
                        "width": 600,
                        "height": 600,
                        "cropper_thickness": "0~0.33",
                        "top_edge": 15,
                        "left_edge": 15,
                        "bottom_edge": 15,
                        "right_edge": 15,
                        "calc_height": 0,
                        "calc_width": 0,
                        "is_break": "否",
                        "layout": "竖排",
                        "amount": 0,
                        "joint_number": "2",
                    }
                    self.insert_data(self.order_table, result.data)
        except Exception as e:
            result.state = False
            result.info = "in get_total_orders() error: {}".format(str(e))
        finally:
            if not result.state:
                logger.debug(result.info)
            orders_with_id = [order for order in result.data if (order.get("order_id") is not None
                                                                 and order.get("order_id") != "")]
            result.data = orders_with_id
            return result

    def upadte_working_info(self, new_values):
        """
        更新当前作业信息表
        :param new_values: 新的值,字典格式
        :return:
        """
        table_name = self.working_info_table
        query = {
            "key": "1"
        }
        return self.update_data(table_name, query, new_values)

    def get_working_order_and_total_orders(self, order_key):
        """
        获取当前作业工单,以及所有的工单
        :return:
        """
        working_order = []
        total_orders = []
        result = self.get_total_orders()
        if not result.state:
            result.data = {
                "working_order": working_order,
                "total_orders": total_orders
            }
            return result
        else:
            total_orders = result.data
        result = self.get_order_by_key(order_key)
        if result.state:
            working_order = result.data
        else:
            result.state = True
            working_order = total_orders[0]
            new_values = {
                "order_used_key": order_key,
                "order_used_id": working_order.get("order_id")
            }
            self.upadte_working_info(new_values)
        result.data = {
            "working_order": [working_order],
            "total_orders": total_orders
        }
        return result

    @staticmethod
    def web_reqeust_dump_data_base():
        """
        界面上点击了数据库备份
        :return:
        """
        return dump_data()

    def get_capfile_data(self):
        """
        获取台布测试的数据表的内容
        :return:
        """
        table_name = self.capfile_data_table
        query = {
            "key": "1"
        }
        return self.get_data_one(table_name, query)

    def get_userinfo_by_name(self, username):
        """
        根据用户名获取用户列表
        :param username:
        :return:
        """
        table_name = self.userinfo_table
        query = {
            "username": username
        }
        return self.get_data_one(table_name, query)

    def update_userinfo(self, query, new_values):
        """
        更新用户信息,比如登录信息
        :param query:
        :param new_values:
        :return:
        """
        return self.update_data(self.userinfo_table, query, new_values)

    def get_cut_speed_by_cropper_thick(self, cropper_thickness):
        """
        根据铜箔厚度获取切割速度
        """
        if cropper_thickness == "0 ~ 0.33":
            key_name = "speed_lv01"
        elif cropper_thickness == "0.34 ~ 1":
            key_name = "speed_lv02"
        elif cropper_thickness == "1.1 ~ 1.5":
            key_name = "speed_lv03"
        elif cropper_thickness == "1.6 ~ 2":
            key_name = "speed_lv04"
        else:
            key_name = "speed_lv05"

        result = self.get_knife_cut_prams()
        if result.state:
            cut_speed = result.data.get(key_name)
            first_cut_speed = int(float(result.data.get("first_cut_rate") * cut_speed))
            result.data = {
                "cut_speed": cut_speed,
                "first_cut_speed": first_cut_speed
            }
        return result

    def get_knife_cut_prams(self):
        """
        获取刀片切割的相关参数
        """
        table_name = self.knife_cut_table
        query = {
            "key": "1"
        }
        return self.get_data_one(table_name, query)

    def update_working_info(self, new_values):
        """
        更新当前工作的信息表
        """
        table_name = self.working_info_table
        query = {
            "key": "1"
        }
        return self.update_data(table_name, query, new_values)

    def get_cutter_cam_check_thres(self):
        """
        获取铡刀阈值相关的参数
        """
        table_name = self.cutter_cam_check_thres
        query = {
            "key": "1"
        }
        return self.get_data_one(table_name, query)

    def update_cutter_cam_check_thres(self, new_values):
        """
        更新铡刀阈值相关的参数
        @param new_values:
        @return:
        """
        table_name = self.cutter_cam_check_thres
        query = {
            "key": "1"
        }
        return self.update_data(table_name, query, new_values)

    def get_trans_mtx(self):
        """
        获取标定矩阵的表数据,单个数据
        @return:
        """
        table_name = self.trans_mtx_table
        query = {
            "key": "1"
        }
        return self.get_data_one(table_name, query)

    def update_trans_mtx(self, new_values):
        """
        更新标定矩阵的表数据
        @param new_values:
        @return:
        """
        table_name = self.trans_mtx_table
        query = {
            "key": "1"
        }
        return self.update_data(table_name, query, new_values)

    def get_offline_zmc_params(self):
        """
        获取裁切机的参数
        @return:
        """
        table_name = self.offline_params_table
        query = {
            "key": "1"
        }
        return self.get_data_one(table_name, query)

    def update_offline_zmc_params(self, new_values):
        """
        更新裁切机的参数
        @return:
        """
        table_name = self.offline_params_table
        query = {
            "key": "1"
        }
        return self.update_data(table_name, query, new_values)

    def get_cam_zmc_params(self):
        """
        获取裁切机的参数
        @return:
        """
        table_name = self.cam_zmc_table
        query = {
            "key": "1"
        }
        return self.get_data_one(table_name, query)

    def update_cam_zmc_params(self, new_values):
        """
        更新裁切机的参数
        @return:
        """
        table_name = self.cam_zmc_table
        query = {
            "key": "1"
        }
        return self.update_data(table_name, query, new_values)

    def get_cbj_zmc_params(self):
        """
        获取裁切机的参数
        @return:
        """
        table_name = self.cbj_zmc_table
        query = {
            "key": "1"
        }
        return self.get_data_one(table_name, query)

    def update_cbj_zmc_params(self, new_values):
        """
        更新裁切机的参数
        @return:
        """
        table_name = self.cbj_zmc_table
        query = {
            "key": "1"
        }
        return self.update_data(table_name, query, new_values)

    def get_cutter_zmc_params(self):
        """
        获取裁切机的参数
        @return:
        """
        table_name = self.cutter_zmc_table
        query = {
            "key": "1"
        }
        return self.get_data_one(table_name, query)

    def update_cutter_zmc_params(self, new_values):
        """
        更新裁切机的参数
        @return:
        """
        table_name = self.cutter_zmc_table
        query = {
            "key": "1"
        }
        return self.update_data(table_name, query, new_values)


if __name__ == '__main__':
    # 1. 相机Ip地址测试
    db = DbHandler()
    result_g = db.get_camera_ip()
    if result_g.state:
        left, middle, right, cutter01, cutter02 = result_g.data
        print(f"左相机ip: {left}")
        print(f"中相机ip: {middle}")
        print(f"右相机ip: {right}")
        print(f"铡刀1号ip: {cutter01}")
        print(f"铡刀2号ip: {cutter02}")
    else:
        print(f"数据库异常: {result_g.info}")

    # 2. update_working_info测试
    result_g = db.get_working_info()
    print(f"result.state = {result_g.state}")
    result_g = db.get_working_info()
    if result_g.state:
        table_used_old = result_g.data.get("table_used")
        print(f"table_used_old = {table_used_old}")
    result_g = db.upadte_working_info({"table_used": 0})
    print(f"result.state = {result_g.state}")
    result_g = db.get_working_info()
    if result_g.state:
        table_used_new = result_g.data.get("table_used")
        print(f"table_used_new = {table_used_new}")
    print(f"result.state = {result_g.state}")

    # 3. get_work_order_and_total_orders
    result_g = db.get_working_order_and_total_orders(100)
    if result_g.state:
        woking_order_g = result_g.data.get("working_order")
        total_orders_g = result_g.data.get("total_orders")
        print(f"working_order = {woking_order_g}")
        print(f"total_orders = {total_orders_g}")
